﻿using System;
using System.Collections.Generic;
using System.Linq;

using Newtonsoft.Json;

namespace LotteryDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 假设所有的中奖概率之和为100%
            Prize defaultPrize = new Prize { Id = -1, Name = "谢谢惠顾", Type = "" };
            List<Prize> prizes = GetPrizes();
            List<PrizePool> prizePools = BuildPrizePool(prizes);

            //Random random = new Random(Environment.TickCount);
            Random random = new Random(GetSeed());

            Dictionary<int, Prize> prizeCache = prizes.ToDictionary(x => x.Id, x => x);
            Dictionary<Prize, int> statistics = new Dictionary<Prize, int>();

            // 抽奖次数
            int lotteryNumber = 999_999;
            for (int i = 0; i < lotteryNumber; i++)
            {
                //double positionOffset = random.Next(0, 100) + random.NextDouble();
                int randomPositionOffset = random.Next(0, 10000);
                //int index = prizePools.FindIndex(x => x.Position > positionOffset);
                PrizePool prizePool = prizePools.FirstOrDefault(x => x.EndPosition > randomPositionOffset && x.StartPosition <= randomPositionOffset);
                Prize prize = (prizePool != null) ? prizeCache[prizePool.Id] : defaultPrize;
                if (statistics.TryGetValue(prize, out int times))
                {
                    statistics[prize] = times + 1;
                }
                else
                {
                    statistics[prize] = 1;
                }
            }

            double dLotteryNumber = Convert.ToDouble(lotteryNumber);
            #region
            //foreach (var prize in prizes)
            //{
            //    double actualProbability = 0;
            //    if (statistics.TryGetValue(prize, out int statistic))
            //    {
            //        actualProbability = Math.Round(statistic / dLotteryNumber * 100, 2);
            //    }

            //    Console.WriteLine(JsonConvert.SerializeObject(new
            //    {
            //        //prize.Id,
            //        prize.Name,
            //        //prize.Type,
            //        prize.Probability,
            //        ActualProbability = actualProbability
            //    }));
            //    //Console.WriteLine($"{prize.Id}\t{prize.Name}\t\t{prize.Type}\t{prize.Probability}\t{actualProbability}");
            //}

            //if (statistics.ContainsKey(defaultPrize))
            //{
            //    var prize = defaultPrize;
            //    double actualProbability = 0;
            //    if (statistics.TryGetValue(prize, out int statistic))
            //    {
            //        actualProbability = Math.Round(statistic / dLotteryNumber * 100, 2);
            //    }

            //    Console.WriteLine(JsonConvert.SerializeObject(new
            //    {
            //        //prize.Id,
            //        prize.Name,
            //        //prize.Type,
            //        prize.Probability,
            //        ActualProbability = actualProbability
            //    }));
            //}
            #endregion

            #region
            List<dynamic> results = new List<dynamic>(prizes.Count + 1);
            foreach (var prize in prizes)
            {
                double actualProbability = 0;
                if (statistics.TryGetValue(prize, out int statistic))
                {
                    actualProbability = Math.Round(statistic / dLotteryNumber * 100, 2);
                }

                results.Add(new 
                {
                    prize.Id,
                    prize.Name,
                    prize.Type,
                    prize.Probability,
                    ActualProbability = actualProbability
                });
            }

            if (statistics.ContainsKey(defaultPrize))
            {
                var prize = defaultPrize;
                double actualProbability = 0;
                if (statistics.TryGetValue(prize, out int statistic))
                {
                    actualProbability = Math.Round(statistic / dLotteryNumber * 100, 2);
                }

                results.Add(new
                {
                    prize.Id,
                    prize.Name,
                    prize.Type,
                    prize.Probability,
                    ActualProbability = actualProbability
                });
            }
            results = results.OrderBy(x => x.ActualProbability).ThenBy(x => x.Probability).ToList();

            string result = JsonConvert.SerializeObject(results);
            Console.WriteLine(result);
            #endregion
        }

        static int GetSeed()
        {
            Random random = new Random();
            byte[] buffer = new byte[4];
            random.NextBytes(buffer);
            return BitConverter.ToInt32(buffer);
        }

        static List<Prize> GetPrizes()
        {
            List<Prize> prizes = new List<Prize>
            {
                new Prize{ Id = 1, Name = "100元现金红包", Type = "实物", Probability = 0.1 },
                new Prize{ Id = 2, Name = "100元京东充值卡", Type = "实物", Probability = 0.4 },
                new Prize{ Id = 3, Name = "50元话费", Type = "虚拟物品", Probability = 1 },
                new Prize{ Id = 4, Name = "永久翻译包", Type = "虚拟物品", Probability = 3 },
                new Prize{ Id = 5, Name = "7天翻译包", Type = "虚拟物品", Probability = 5 },
                new Prize{ Id = 6, Name = "230颗蓝钻", Type = "虚拟物品", Probability = 5 },
                new Prize{ Id = 7, Name = "1个月会员", Type = "虚拟物品", Probability = 8 },
                new Prize{ Id = 8, Name = "100粉钻", Type = "虚拟物品", Probability = 10 },
                new Prize{ Id = 9, Name = "20粉钻", Type = "虚拟物品", Probability = 20 },
                new Prize{ Id = 10, Name = "5粉钻", Type = "虚拟物品", Probability = 47.5 },
                new Prize{ Id = 11, Name = "苹果13Pro", Type = "实物", Probability = 0 },
                new Prize{ Id = 12, Name = "华为P40", Type = "实物", Probability = 0 },
            };

            return prizes;
        }

        static List<PrizePool> BuildPrizePool(List<Prize> prizes)
        {
            List<PrizePool> prizePools = new List<PrizePool>(prizes.Count(x => x.Probability > 0));
            int minPositionOffset = 0;

            foreach (var prize in prizes)
            {
                if (prize.Probability <= 0) continue;

                var maxPositionOffset = minPositionOffset + Convert.ToInt32(Math.Round(prize.Probability, 2) * 100); // 精确到万分之一
                prizePools.Add(new PrizePool { Id = prize.Id, EndPosition = maxPositionOffset, StartPosition = minPositionOffset });
                minPositionOffset = maxPositionOffset;
            }

            //prizePools.Reverse();

            return prizePools;
        }
    }
}
